package pw.jeb;



import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @Author Eric
 * @Desc
 * @Date Create in 18-2-7 下午3:13
 * @Modified By
 */
public class AnnotatedFinder {

    /** Cache event bus subscriber methods for each class. */
    private static final ConcurrentMap<Class<?>, Map<String, Set<EventHandler>>> SUBSCRIBERS_CACHE =
            new ConcurrentHashMap<Class<?>, Map<String, Set<EventHandler>>>();

    /**
     * Load all methods annotated with {@link Subscribe} into their respective caches for the
     * specified class.
     */
    private static void loadAnnotatedMethods(Object listener,Map<String, Set<EventHandler>> subscriberMethods) {
        Class<?> listenerClass = listener.getClass();

        for (Method method : listenerClass.getDeclaredMethods()) {
            // The compiler sometimes creates synthetic bridge methods as part of the
            // type erasure process. As of JDK8 these methods now include the same
            // annotations as the original declarations. They should be ignored for
            // subscribe/produce.
            if (method.isBridge()) {
                continue;
            }
            if (method.isAnnotationPresent(Subscribe.class)) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length != 1) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires "
                            + parameterTypes.length + " arguments.  Methods must require a single argument.");
                }

                Class<?> eventType = parameterTypes[0];
                if (!Event.class.isAssignableFrom(eventType)) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
                            + " but must extend the 'Event' ");
                }

                if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
                            + " but is not 'public'.");
                }

                Subscribe subscribe = method.getAnnotation(Subscribe.class);
                String [] types = subscribe.value();
                for(String type : types){
                    Set<EventHandler> methods = subscriberMethods.get(eventType);
                    if (methods == null) {
                        methods = new HashSet<EventHandler>();
                        subscriberMethods.put(type, methods);
                    }
                    methods.add(new EventHandler(listener,method));
                }

            }
        }
        SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
    }

    /**
     * 返回的是单个类所对应的type以及Handler
     * @param listener
     * @return
     */
    static Map<String, Set<EventHandler>> findAllSubscribers(Object listener) {
        Class<?> listenerClass = listener.getClass();

        Map<String, Set<EventHandler>> methods = SUBSCRIBERS_CACHE.get(listenerClass);
        if (null == methods) {
            methods = new HashMap<String, Set<EventHandler>>();
            loadAnnotatedMethods(listener, methods);
        }

        return methods;
    }

    private AnnotatedFinder() {
        // No instances.
    }

}
